package com.glSto.controller;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.net.ssl.*;
import javax.crypto.Cipher;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 门禁系统API接口 - 整合所有功能的单一文件实现
 */
@RestController
@RequestMapping("/api/access-control")
public class AccessControlSystem {
    // 日志记录器
    private static final Logger mainDataLogger = LoggerFactory.getLogger("com.glSto.controller.AccessControlApi");
//    private static final Gson gson = new Gson();

    private static final Gson gson = new GsonBuilder()
            .setDateFormat("yyyy-MM-dd HH:mm:ss")  // 支持空格分隔的日期格式
            .create();

    // 常量定义
    private static final String TOKEN_API_URL = "https://10.105.0.5:443/evo-apigw/evo-oauth/1.0.0/oauth/extend/token";
    private static final String SWING_CARD_RECORD_API_URL = "https://10.105.0.5:443/evo-apigw/evo-visitor/1.0.0/card/accessControl/swingCardRecord/visitor/bycondition/combined";
    private static final String FIXED_CLIENT_ID = "wuyanggao";
    private static final String FIXED_CLIENT_SECRET = "42bec152-8f04-476a-9aec-e7d616ff3cb3";
    private static final String FIXED_GRANT_TYPE = "password";
    private static final String OAUTH_BEARER_PREFIX = "Bearer ";
    private static final int FIXED_PAGE_SIZE = 100;

    // 线程池配置
    private static final int CPU_CORES = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_CORES * 2;
    private static final int MAX_POOL_SIZE = CPU_CORES * 4;
    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(50),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    // 数据库连接池
    private static final DriverManagerDataSource dataSource;
    private static final JdbcTemplate jdbcTemplate;
    private static final DataSourceTransactionManager transactionManager;

    static {
        // 初始化数据库连接池
        dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
        dataSource.setUrl("jdbc:oracle:thin:@//10.105.45.14:1521/GLPC_CDB01_PDB06");
        dataSource.setUsername("ats");
        dataSource.setPassword("Power4005");

        // 设置连接属性
        Properties connectionProps = new Properties();
        connectionProps.setProperty("oracle.net.CONNECT_TIMEOUT", "5000");
        dataSource.setConnectionProperties(connectionProps);

        jdbcTemplate = new JdbcTemplate(dataSource);
        transactionManager = new DataSourceTransactionManager(dataSource);

        // HTTPS证书处理
        try {
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        public X509Certificate[] getAcceptedIssuers() { return null; }
                        public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {}
                        public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {}
                    }
            };
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
            HostnameVerifier allHostsValid = (hostname, session) -> true;
            HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
        } catch (Exception e) {
            mainDataLogger.error("SSL初始化异常", e);
            throw new RuntimeException("SSL初始化失败，影响HTTPS请求", e);
        }
    }

    /**
     * 单一接口：同步门禁刷卡记录全流程
     * 一次调用完成从获取Token到数据同步的所有操作
     */
    @PostMapping("/AccessControlSystem")
    public ResponseEntity<ApiResult> syncAllRecords(@RequestBody SyncRequest request) {
        long startTime = System.currentTimeMillis();
        ApiResult result = new ApiResult();

        try {
            // 1. 验证请求参数
            if (!validateRequest(request)) {
                result.setSuccess(false);
                result.setMessage("请求参数不完整，必须包含username、password、startSwingTime和endSwingTime");
                return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
            }

            // 2. 获取访问令牌
            mainDataLogger.info("开始获取访问令牌...");
            TokenData tokenData = getOAuthAccessToken(request.getUsername(), request.getPassword());
            String accessToken = tokenData.getAccess_token();
            mainDataLogger.info("访问令牌获取成功，有效期：{}秒", tokenData.getExpires_in());

            // 3. 构造查询请求
            SwingCardRecordRequest recordRequest = new SwingCardRecordRequest(
                    request.getStartSwingTime(), request.getEndSwingTime());
            recordRequest.setCardNumber(request.getCardNumber());
            recordRequest.setPersonName(request.getPersonName());
            recordRequest.setChannelCode(request.getChannelCode());

            // 4. 执行全量数据同步
            mainDataLogger.info("开始同步门禁记录，时间范围：{} 至 {}",
                    request.getStartSwingTime(), request.getEndSwingTime());
            queryAllSwingCardRecordsWithHighPerformance(recordRequest, accessToken);

            // 5. 准备成功响应
            long duration = (System.currentTimeMillis() - startTime) / 1000;
            result.setSuccess(true);
            result.setMessage("门禁记录同步完成");
            result.setDurationSeconds(duration);
            result.setTokenExpiresIn(tokenData.getExpires_in());

            return new ResponseEntity<>(result, HttpStatus.OK);

        } catch (Exception e) {
            mainDataLogger.error("同步过程发生异常", e);
            result.setSuccess(false);
            result.setMessage("同步失败：" + e.getMessage());
            result.setDurationSeconds((System.currentTimeMillis() - startTime) / 1000);
            return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    // 验证请求参数
    private boolean validateRequest(SyncRequest request) {
        return request != null
                && request.getUsername() != null
                && request.getPassword() != null
                && request.getStartSwingTime() != null
                && request.getEndSwingTime() != null;
    }

    // 带OAuth鉴权的POST请求方法
    private String sendHttpsPostWithOAuth(String apiUrl, Object requestParam, String accessToken) {
        if (accessToken == null || accessToken.trim().isEmpty()) {
            String errMsg = "OAuth 2.0鉴权失败：access_token为空";
            mainDataLogger.error(errMsg);
            throw new IllegalArgumentException(errMsg);
        }
        StringBuilder response = new StringBuilder();
        HttpsURLConnection connection = null;
        OutputStream outputStream = null;
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            URL url = new URL(apiUrl);
            connection = (HttpsURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(10000);
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            connection.setRequestProperty("Accept", "application/json");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            connection.setRequestProperty("Authorization", OAUTH_BEARER_PREFIX + accessToken);

            String rawJsonRequest = gson.toJson(requestParam);
            mainDataLogger.debug("接口[{}]请求体：\n{}", apiUrl, rawJsonRequest);

            outputStream = connection.getOutputStream();
            outputStream.write(rawJsonRequest.getBytes(StandardCharsets.UTF_8));
            outputStream.flush();

            int responseCode = connection.getResponseCode();
            inputStream = (responseCode >= 200 && responseCode < 300) ? connection.getInputStream() : connection.getErrorStream();

            if (responseCode >= 300) {
                String errMsg = String.format("接口[%s]请求失败，响应码：%d", apiUrl, responseCode);
                mainDataLogger.error(errMsg);
                response.append(errMsg).append("\n");
            }

            reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            String line;
            while ((line = reader.readLine()) != null) response.append(line);

            mainDataLogger.debug("接口[{}]响应内容：\n{}", apiUrl, response.toString());
        } catch (IOException e) {
            mainDataLogger.error("接口[{}]POST请求异常", apiUrl, e);
            response.append("请求异常：").append(e.getMessage());
        } finally {
            closeResources(reader, inputStream, connection);
            try {
                if (outputStream != null) outputStream.close();
            } catch (IOException e) {
                mainDataLogger.error("POST输出流关闭异常", e);
            }
        }
        return response.toString();
    }

    // GET请求方法
    private String sendHttpsGetRequest(String apiUrl) {
        StringBuilder response = new StringBuilder();
        HttpsURLConnection connection = null;
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            URL url = new URL(apiUrl);
            connection = (HttpsURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(5000);
            connection.setRequestProperty("Accept", "application/json");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");

            int responseCode = connection.getResponseCode();
            inputStream = (responseCode == HttpsURLConnection.HTTP_OK) ? connection.getInputStream() : connection.getErrorStream();

            if (responseCode != HttpsURLConnection.HTTP_OK) {
                String errMsg = "GET请求失败，响应码：" + responseCode;
                mainDataLogger.error(errMsg);
                response.append(errMsg).append("\n");
            }

            reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            String line;
            while ((line = reader.readLine()) != null) response.append(line);
        } catch (IOException e) {
            mainDataLogger.error("GET请求异常", e);
            response.append("GET请求异常：").append(e.getMessage());
        } finally {
            closeResources(reader, inputStream, connection);
        }
        return response.toString();
    }

    // 获取access_token方法
    private TokenData getOAuthAccessToken(String username, String plainPassword) throws Exception {
        String publicKeyApi = "https://10.105.0.5:443/evo-apigw/evo-oauth/1.0.0/oauth/public-key";
        String publicKeyResponse = sendHttpsGetRequest(publicKeyApi);
        String publicKey = extractPublicKey(publicKeyResponse);
        if (publicKey == null) {
            throw new RuntimeException("获取公钥失败，无法加密密码");
        }

        String encryptedPassword = encrypt(plainPassword, publicKey);
        mainDataLogger.info("密码加密完成");

        TokenRequest tokenRequest = new TokenRequest(username, encryptedPassword, publicKey);
        String tokenResponseJson = sendHttpsPostWithOAuth(TOKEN_API_URL, tokenRequest, "NO_AUTH_NEEDED");
        TokenResponse tokenResponse = gson.fromJson(tokenResponseJson, TokenResponse.class);

        if (tokenResponse == null || !tokenResponse.isSuccess() || tokenResponse.getData() == null) {
            String errMsg = String.format("获取access_token失败，错误码：%s，错误信息：%s",
                    tokenResponse.getCode(), tokenResponse.getErrMsg());
            mainDataLogger.error(errMsg);
            throw new RuntimeException(errMsg);
        }

        return tokenResponse.getData();
    }

    // 刷卡记录查询方法
    private SwingCardRecordResponse querySwingCardRecord(SwingCardRecordRequest request, String accessToken) {
        String responseJson = sendHttpsPostWithOAuth(SWING_CARD_RECORD_API_URL, request, accessToken);
        return gson.fromJson(responseJson, SwingCardRecordResponse.class);
    }

    /**
     * 高性能多线程处理
     * 1. 批量API请求
     * 2. 批量数据库插入
     * 3. 连接池复用连接
     */
    private void queryAllSwingCardRecordsWithHighPerformance(SwingCardRecordRequest request, String accessToken)
            throws InterruptedException, ExecutionException {

        // 初始化中间表
        deleteZJB();

        // 1. 首次查询获取总页数
        SwingCardRecordResponse firstResponse = querySwingCardRecord(request, accessToken);
        if (firstResponse == null || !firstResponse.isSuccess() || firstResponse.getData() == null) {
            String errMsg = "首次查询失败：" + (firstResponse != null ? firstResponse.getErrMsg() : "未知错误");
            mainDataLogger.error(errMsg);
            throw new RuntimeException(errMsg);
        }

        SwingCardRecordData firstPageData = firstResponse.getData();
        int totalPage = firstPageData.getTotalPage();
        int totalRows = firstPageData.getTotalRows();

        mainDataLogger.info("=== 分页查询初始化完成 ===");
        mainDataLogger.info("总记录数：{} 条，总页数：{} 页，线程数：{}",
                totalRows, totalPage, CORE_POOL_SIZE);
        mainDataLogger.info("=========================\n");

        // 2. 创建线程安全的队列存储分页数据
        BlockingQueue<SwingCardRecordItem[]> dataQueue = new LinkedBlockingQueue<>(20);
        CountDownLatch allPagesProcessed = new CountDownLatch(totalPage);
        AtomicInteger successCount = new AtomicInteger(0);
        AtomicInteger failCount = new AtomicInteger(0);

        // 3. 启动消费者线程 - 负责批量插入数据库
        for (int i = 0; i < CORE_POOL_SIZE; i++) {
            executor.submit(() -> {
                try {
                    while (true) {
                        // 从队列获取数据，超时退出
                        SwingCardRecordItem[] records = dataQueue.poll(10, TimeUnit.SECONDS);
                        if (records == null) break;

                        // 批量插入当前页数据
                        int inserted = batchInsertRecords(records);

                        successCount.addAndGet(inserted);
                        failCount.addAndGet(records.length - inserted);
                        allPagesProcessed.countDown();
                        mainDataLogger.info("线程[{}]完成一页处理，成功插入{}条，失败{}条",
                                Thread.currentThread().getName(), inserted, records.length - inserted);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    mainDataLogger.error("消费者线程被中断", e);
                }
            });
        }

        // 4. 并行获取所有分页数据
        List<CompletableFuture<Void>> pageFutures = new ArrayList<>();

        // 先添加第一页数据
        dataQueue.put(firstPageData.getPageData());
        mainDataLogger.info("已添加第1页数据到处理队列");

        // 并行获取剩余页数据
        for (int pageNum = 2; pageNum <= totalPage; pageNum++) {
            final int currentPage = pageNum;
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    SwingCardRecordRequest pageRequest = new SwingCardRecordRequest(
                            request.getStartSwingTime(), request.getEndSwingTime());
                    pageRequest.setPageNum(currentPage);
                    pageRequest.setCardNumber(request.getCardNumber());
                    pageRequest.setPersonName(request.getPersonName());
                    pageRequest.setChannelCode(request.getChannelCode());

                    SwingCardRecordResponse pageResponse = querySwingCardRecord(pageRequest, accessToken);
                    if (pageResponse == null || !pageResponse.isSuccess() || pageResponse.getData() == null) {
                        String errMsg = String.format("第%d页查询失败", currentPage);
                        mainDataLogger.error(errMsg);
                        return;
                    }

                    dataQueue.put(pageResponse.getData().getPageData());
                    mainDataLogger.info("已添加第{}页数据到处理队列", currentPage);
                } catch (Exception e) {
                    mainDataLogger.error("获取第" + currentPage + "页数据异常", e);
                }
            }, executor);

            pageFutures.add(future);
        }

        // 等待所有页面数据获取完成
        CompletableFuture.allOf(pageFutures.toArray(new CompletableFuture[0])).join();

        // 5. 等待所有页面处理完成
        allPagesProcessed.await();
        mainDataLogger.info("\n=== 所有分页数据处理完成 ===");
        mainDataLogger.info("总处理记录数：{} 条", successCount.get() + failCount.get());
        mainDataLogger.info("成功插入：{} 条", successCount.get());
        mainDataLogger.info("插入失败：{} 条", failCount.get());

        // 6. 合并到正式表
        INTOFKJL();

        // 关闭线程池
        executor.shutdown();
    }

    // 批量插入记录到中间表
    private int batchInsertRecords(SwingCardRecordItem[] records) {
        if (records == null || records.length == 0) {
            return 0;
        }

        // 开启事务
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            String sql = "INSERT INTO MJXTJK_FKJL_ZJB (" +
                    "ID, DEVICE_CODE, DEVICE_NAME, CHANNEL_CODE, CHANNEL_NAME, " +
                    "SWING_TIME, PERSON_NAME, PERSON_ID, CARD_NUMBER, OPEN_TYPE, " +
                    "OPEN_RESULT, CARD_STATUS, ENTER_OR_EXIT, REMARK, CREATE_TIME, UPDATE_TIME) " +
                    "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, SYSDATE, SYSDATE)";

            // 批量预处理
            jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
                @Override
                public void setValues(PreparedStatement ps, int i) throws SQLException {
                    SwingCardRecordItem item = records[i];

                    ps.setLong(1, item.getId());
                    ps.setString(2, item.getDeviceCode());
                    ps.setString(3, item.getDeviceName());
                    ps.setString(4, item.getChannelCode());
                    ps.setString(5, item.getChannelName());
                    ps.setTimestamp(6, item.getSwingTime() != null ?
                            new Timestamp(item.getSwingTime().getTime()) : null);
                    ps.setString(7, item.getPersonName());
                    ps.setLong(8, item.getPersonId());
                    ps.setString(9, item.getCardNumber());
                    ps.setInt(10, item.getOpenType());
                    ps.setInt(11, item.getOpenResult());
                    ps.setInt(12, item.getCardStatus());
                    ps.setInt(13, item.getEnterOrExit());
                    ps.setString(14, item.getRemark() != null ? item.getRemark().replace("'", "''") : null);
                }

                @Override
                public int getBatchSize() {
                    return records.length;
                }
            });

            // 提交事务
            transactionManager.commit(status);
            return records.length;
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(status);
            mainDataLogger.error("批量插入失败", e);
            return 0;
        }
    }

    // 删除中间表数据
    private boolean deleteZJB() {
        try {
            jdbcTemplate.update("DELETE FROM MJXTJK_FKJL_ZJB");
            return true;
        } catch (Exception e) {
            mainDataLogger.error("删除中间表失败", e);
            return false;
        }
    }

    // 中间表数据插入正式表
    private boolean INTOFKJL() {
        String sql = "INSERT INTO MJXTJK_FKJL (" +
                "ID, DEVICE_CODE, DEVICE_NAME, CHANNEL_CODE, CHANNEL_NAME, " +
                "SWING_TIME, PERSON_NAME, PERSON_ID, CARD_NUMBER, OPEN_TYPE, " +
                "OPEN_RESULT, CARD_STATUS, ENTER_OR_EXIT, REMARK, CREATE_TIME, " +
                "UPDATE_TIME, CURRENT_PAGE, TOTAL_PAGE, PAGE_SIZE, TOTAL_ROWS, " +
                "ENABLE_CLOUD_DB) " +
                "SELECT " +
                "ID, DEVICE_CODE, DEVICE_NAME, CHANNEL_CODE, CHANNEL_NAME, " +
                "SWING_TIME, PERSON_NAME, PERSON_ID, CARD_NUMBER, OPEN_TYPE, " +
                "OPEN_RESULT, CARD_STATUS, ENTER_OR_EXIT, REMARK, CREATE_TIME, " +
                "UPDATE_TIME, CURRENT_PAGE, TOTAL_PAGE, PAGE_SIZE, TOTAL_ROWS, " +
                "ENABLE_CLOUD_DB " +
                "FROM MJXTJK_FKJL_ZJB zjb " +
                "WHERE NOT EXISTS (" +
                "    SELECT 1 FROM MJXTJK_FKJL fkjl WHERE fkjl.ID = zjb.ID" +
                ")";

        try {
            int rowsAffected = jdbcTemplate.update(sql);
            mainDataLogger.info("已将" + rowsAffected + "条新记录从中间表插入正式表");
            return rowsAffected > 0;
        } catch (Exception e) {
            mainDataLogger.error("插入正式表失败", e);
            return false;
        }
    }

    // 工具方法：提取公钥
    private String extractPublicKey(String jsonResponse) {
        try {
            ApiResponse response = gson.fromJson(jsonResponse, ApiResponse.class);
            if (response != null && response.isSuccess() && response.getData() != null) {
                String publicKey = response.getData().getPublicKey();
                mainDataLogger.info("提取到公钥：{}", publicKey);
                return publicKey;
            } else {
                mainDataLogger.error("提取公钥失败，响应：{}", jsonResponse);
                return null;
            }
        } catch (Exception e) {
            mainDataLogger.error("公钥解析异常", e);
            return null;
        }
    }

    // 工具方法：加密密码
    private String encrypt(String password, String publicKey) throws Exception {
        byte[] decodedPublicKey = java.util.Base64.getDecoder().decode(publicKey);
        RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
                .generatePublic(new X509EncodedKeySpec(decodedPublicKey));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);
        byte[] inputBytes = password.getBytes(StandardCharsets.UTF_8);
        int inputLength = inputBytes.length;
        int MAX_ENCRYPT_BLOCK = 117;
        int offSet = 0;
        byte[] resultBytes = new byte[0];
        byte[] cache;
        while (inputLength - offSet > 0) {
            if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(inputBytes, offSet, MAX_ENCRYPT_BLOCK);
                offSet += MAX_ENCRYPT_BLOCK;
            } else {
                cache = cipher.doFinal(inputBytes, offSet, inputLength - offSet);
                offSet = inputLength;
            }
            resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
            System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
        }
        return java.util.Base64.getEncoder().encodeToString(resultBytes);
    }

    // 工具方法：关闭资源
    private void closeResources(BufferedReader reader, InputStream inputStream, HttpsURLConnection connection) {
        try {
            if (reader != null) reader.close();
            if (inputStream != null) inputStream.close();
            if (connection != null) connection.disconnect();
        } catch (IOException e) {
            mainDataLogger.error("资源关闭异常", e);
        }
    }

    // 内部实体类定义
    public static class SyncRequest {
        private String username;
        private String password;
        private String startSwingTime;
        private String endSwingTime;
        private String cardNumber;
        private String personName;
        private String channelCode;

        // Getters and Setters
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
        public String getPassword() { return password; }
        public void setPassword(String password) { this.password = password; }
        public String getStartSwingTime() { return startSwingTime; }
        public void setStartSwingTime(String startSwingTime) { this.startSwingTime = startSwingTime; }
        public String getEndSwingTime() { return endSwingTime; }
        public void setEndSwingTime(String endSwingTime) { this.endSwingTime = endSwingTime; }
        public String getCardNumber() { return cardNumber; }
        public void setCardNumber(String cardNumber) { this.cardNumber = cardNumber; }
        public String getPersonName() { return personName; }
        public void setPersonName(String personName) { this.personName = personName; }
        public String getChannelCode() { return channelCode; }
        public void setChannelCode(String channelCode) { this.channelCode = channelCode; }
    }

    public static class ApiResult {
        private boolean success;
        private String message;
        private long durationSeconds;
        private long tokenExpiresIn;

        // Getters and Setters
        public boolean isSuccess() { return success; }
        public void setSuccess(boolean success) { this.success = success; }
        public String getMessage() { return message; }
        public void setMessage(String message) { this.message = message; }
        public long getDurationSeconds() { return durationSeconds; }
        public void setDurationSeconds(long durationSeconds) { this.durationSeconds = durationSeconds; }
        public long getTokenExpiresIn() { return tokenExpiresIn; }
        public void setTokenExpiresIn(long tokenExpiresIn) { this.tokenExpiresIn = tokenExpiresIn; }
    }

    private static class TokenRequest {
        private final String grant_type;
        private final String username;
        private final String password;
        private final String client_id;
        private final String client_secret;
        private final String public_key;

        public TokenRequest(String username, String encryptedPassword, String publicKey) {
            this.grant_type = FIXED_GRANT_TYPE;
            this.username = username;
            this.password = encryptedPassword;
            this.client_id = FIXED_CLIENT_ID;
            this.client_secret = FIXED_CLIENT_SECRET;
            this.public_key = publicKey;
        }
    }

    private static class TokenResponse {
        private boolean success;
        private TokenData data;
        private String code;
        private String errMsg;

        public boolean isSuccess() { return success; }
        public TokenData getData() { return data; }
        public String getCode() { return code; }
        public String getErrMsg() { return errMsg; }
    }

    private static class TokenData {
        private String access_token;
        private String refresh_token;
        private long expires_in;
        private String token_type;

        public String getAccess_token() { return access_token; }
        public String getRefresh_token() { return refresh_token; }
        public long getExpires_in() { return expires_in; }
        public String getToken_type() { return token_type; }
    }

    private static class SwingCardRecordRequest {
        private int pageNum = 1;
        private final int pageSize = FIXED_PAGE_SIZE;
        private String startSwingTime;
        private String endSwingTime;
        private String cardNumber;
        private String personName;
        private String channelCode;

        public SwingCardRecordRequest(String startSwingTime, String endSwingTime) {
            this.startSwingTime = startSwingTime;
            this.endSwingTime = endSwingTime;
        }

        public void setPageNum(int pageNum) {
            if (pageNum < 1) {
                throw new IllegalArgumentException("pageNum不能小于1");
            }
            this.pageNum = pageNum;
        }

        public void setCardNumber(String cardNumber) { this.cardNumber = cardNumber; }
        public void setPersonName(String personName) { this.personName = personName; }
        public void setChannelCode(String channelCode) { this.channelCode = channelCode; }

        public int getPageNum() { return pageNum; }
        public int getPageSize() { return pageSize; }
        public String getStartSwingTime() { return startSwingTime; }
        public String getEndSwingTime() { return endSwingTime; }
        public String getCardNumber() { return cardNumber; }
        public String getPersonName() { return personName; }
        public String getChannelCode() { return channelCode; }
    }

    private static class SwingCardRecordResponse {
        private boolean success;
        private String code;
        private String errMsg;
        private SwingCardRecordData data;

        public boolean isSuccess() { return success; }
        public String getCode() { return code; }
        public String getErrMsg() { return errMsg; }
        public SwingCardRecordData getData() { return data; }
    }

    private static class SwingCardRecordData {
        private SwingCardRecordItem[] pageData;
        private int currentPage;
        private int totalPage;
        private int pageSize;
        private int totalRows;
        private boolean enableCloudDB;

        public SwingCardRecordItem[] getPageData() { return pageData; }
        public int getCurrentPage() { return currentPage; }
        public int getTotalPage() { return totalPage; }
        public int getPageSize() { return pageSize; }
        public int getTotalRows() { return totalRows; }
        public boolean isEnableCloudDB() { return enableCloudDB; }
    }

    private static class SwingCardRecordItem {
        private long id;
        private String deviceCode;
        private String deviceName;
        private String channelCode;
        private String channelName;
        private Date swingTime;
        private String personName;
        private String personCode;
        private long personId;
        private String cardNumber;
        private int openType;
        private int openResult;
        private int cardStatus;
        private int enterOrExit;
        private String remark;

        public long getId() { return id; }
        public String getDeviceCode() { return deviceCode; }
        public String getDeviceName() { return deviceName; }
        public String getChannelCode() { return channelCode; }
        public String getChannelName() { return channelName; }
        public Date getSwingTime() { return swingTime; }
        public String getPersonName() { return personName; }
        public String getPersonCode() { return personCode; }
        public long getPersonId() { return personId; }
        public String getCardNumber() { return cardNumber; }
        public int getOpenType() { return openType; }
        public int getOpenResult() { return openResult; }
        public int getCardStatus() { return cardStatus; }
        public int getEnterOrExit() { return enterOrExit; }
        public String getRemark() { return remark; }
    }

    private static class ApiResponse {
        private boolean success;
        private Data data;
        private String code;
        private String errMsg;

        public boolean isSuccess() { return success; }
        public Data getData() { return data; }
    }

    private static class Data {
        private String publicKey;

        public String getPublicKey() { return publicKey; }
    }
}
